package com.shopping.service.service.login.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shopping.common.core.constant.ErrorCodeEnum;
import com.shopping.common.core.constant.JwtEnum;
import com.shopping.common.core.object.ResponseResult;
import com.shopping.common.core.object.TokenData;
import com.shopping.common.core.util.JwtUtil;
import com.shopping.common.core.util.RedisKeyUtil;
import com.shopping.service.config.ApplicationConfig;
import com.shopping.common.sp.model.user.User;
import com.shopping.service.service.login.LoginService;
import com.shopping.service.mapper.UserMapper;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


/**
 * @program: shopping-mall
 * @ClassName LoginServiceImpl
 * @description:
 * @author: zlw
 * @create: 2024-03-27 22:41
 * @Version 1.0
 **/
@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ApplicationConfig applicationConfig;

    @Override
    public ResponseResult<JSONObject> login(String userName, String password) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUserName,userName);
        wrapper.eq(User::getPassWord,password);
        User user = userMapper.selectOne(wrapper);

       if(user == null){
           return ResponseResult.error(ErrorCodeEnum.LOGIN_ERRROR.name(),ErrorCodeEnum.LOGIN_ERRROR.getErrorMessage());
       }

       //存储登录信息
        JSONObject jsonData = buildLoginData(user);
        return ResponseResult.success("登陆成功",jsonData);
    }

    private  JSONObject buildLoginData(User user) {
        //获取request先
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //从请求头拿出token
        String token = request.getHeader("Authorization");
        //构建返回对象
        JSONObject jsonData = new JSONObject();
        //如果已有登录信息
        if(StrUtil.isNotBlank(token)){
            //解析token获取sessionId
            DecodedJWT decodedJWT = JwtUtil.parseToken(token);
            String sessionId = (String) decodedJWT.getClaim(JwtEnum.JWT_CLAIM.getKeyName()).asMap().get("sessionId");
            //构建完整的sessionIdKey
            String sessionIdKey = RedisKeyUtil.makeSessionIdKey(sessionId);
            RBucket<String> sessionDataBucket = redissonClient.getBucket(sessionIdKey);
            if(sessionDataBucket.isExists()) {
                //更新redis的登录数据的过期时间
                sessionDataBucket.expire(applicationConfig.getSessionExpirationSeconds(), TimeUnit.SECONDS);
                //插入返回数据
                jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token);
                return jsonData;
            }
        }
        //redis里没有改存储对象,则重新操作(兜底)
        //token过期也算
        Map<String,Object> claims = new HashMap<>(3);
        String sessionId = "login-" + user.getUserName() + "-" + UUID.randomUUID();
        //在token的payload中放入当前sessionId,方便后面验证拿来拼接redis的sessionIdKey
        claims.put("sessionId",sessionId);
        //进行去重判断
        String sessionKey = RedisKeyUtil.makeSessionIdKey(sessionId);
        RBucket<String> bucket = redissonClient.getBucket(sessionKey);

        //生成token
        token = JwtUtil.generateToken(claims,applicationConfig.getExpiration(),applicationConfig.getTokenSignKey());
        //构建返回数据
        jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME,token);
        //构建redis插入的数据
        TokenData tokenData = new TokenData();
        tokenData.setUserId(user.getUserId());
        tokenData.setUserName(user.getUserName());
        //redis操作
        String sessionData = JSONObject.toJSONString(tokenData, SerializerFeature.NotWriteDefaultValue);
        bucket.set(sessionData);
        bucket.expire(applicationConfig.getSessionExpirationSeconds(), TimeUnit.SECONDS);
        //这里手动将TokenData存入request,便于OperationLogAspect统一处理操作日志
        TokenData.addRequest(tokenData);
        return jsonData;
    }
}
